#include "Building.h"
#include "DXUT.h"

#define _USE_MATH_DEFINES
#include <math.h>
void Building::Generate(D3DXVECTOR3 centre, D3DXVECTOR3 area)
{
	m_vPos = centre;
	m_vArea = area;
	m_Step=0;
	m_MaxStep=100;
	m_Stepped=false;
	m_Type = rand()%4;

	
	switch(m_Type)
	{
		case SIMPLE:
		GenSimple();
		break;
		case BLOCKY:
		GenBlocky();
		break;
		case MODERN:
		GenModern();
		break;
		case CLASSIC:
		GenClassic();
		break;
	}
	

	//GenSimple();
	//GenBlocky();
	//GenModern();
	//GenClassic();
	HRESULT hr;
	V(m_pMesh->OptimizeInplace(D3DXMESHOPT_COMPACT ,0,0,0,0));
	V(D3DXComputeNormals(m_pMesh,0));
}

void Building::GenSimple()
{	
	HRESULT hr;
	int max_height = m_vArea.y;
	float height = ((rand()%(max_height/2)+1)+(max_height/2));
	LPD3DXMESH mesh;
	D3DXCreateBox(TheD3DApp::Instance()->GetD3DDev(),m_vArea.x,height,m_vArea.z,&mesh,0);
	D3DXMATRIX T[1];
	LPD3DXMESH M[1];
	M[0]=mesh;
	D3DXMatrixTransformation(&T[0],0,0,0,0,0,&D3DXVECTOR3(0,height/2,0));
	V(D3DXConcatenateMeshes(M,1,D3DXMESH_MANAGED,T,0,NULL,TheD3DApp::Instance()->GetD3DDev(),&m_pMesh));
}

void Building::GenBlocky()
{
	int skipcount=0;
	float left,right,front,back,height;
	int half_widthHun = (m_vArea.x*100)/2;
	int half_depthHun = (m_vArea.z*100)/2;
	int max_height;
	bool skip=false;
	HRESULT hr;
	LPD3DXMESH mesh;
	D3DXMATRIX TransMatrix[2];
	LPD3DXMESH MeshBuffer[2];
	int Tiers = (rand()%5)+2;
	if(m_Step==0)
	{
		max_height = m_vArea.y;
		height=(((rand()%(max_height/2))+1)+(max_height/2));
		m_LastTierHeight=height;
		m_Height=height;
		D3DXCreateBox(TheD3DApp::Instance()->GetD3DDev(),m_vArea.x,0.01,m_vArea.z,&m_pMesh,0);
		m_Step++;
	}
	for (unsigned int i=1;i<Tiers;++i)
	{

		left = (float)(rand()%half_widthHun+1)/100;
		right = (float)(rand()%half_widthHun+1)/100;
		front = (float)(rand()%half_depthHun+1)/100;
		back = (float)(rand()%half_depthHun+1)/100;
		max_height = m_LastTierHeight;
		height=((rand()%(max_height/2)+1)+(max_height/2));
		if (left <= m_maxLeft && right <= m_maxRight && front <= m_maxFront && back <= m_maxBack) 
		{skip = true;}


		if(height>2 && !skip)
		{
			m_maxLeft = max(left, m_maxLeft);
			m_maxRight = max(right, m_maxRight);
			m_maxFront = max(front, m_maxFront);
			m_maxBack = max(back, m_maxBack);


			m_LastTierHeight=height;
			D3DXCreateBox(TheD3DApp::Instance()->GetD3DDev(),left+right,height,front+back,&mesh,0);

			float width = left+right;
			float hwidth =width/2;
			float tranx = hwidth-left;

			float depth = front+back;
			float hdepth = depth/2;
			float tranz = hdepth-front;

			MeshBuffer[0]=m_pMesh;
			MeshBuffer[1]=mesh;
			D3DXMatrixIdentity(&TransMatrix[0]);
			D3DXMatrixTransformation(&TransMatrix[1],0,0,0,0,0,&D3DXVECTOR3(tranx,height/2,tranz));

			V(D3DXConcatenateMeshes(MeshBuffer,2,D3DXMESH_MANAGED,TransMatrix,0,NULL,TheD3DApp::Instance()->GetD3DDev(),&m_pMesh));
			
			m_Step++;
			m_NumSubsets++;
		}
		if(skip)
		{
			skipcount++;

			if(skipcount>5)
			{
				m_Step++;
				skipcount=0;
			}
		}
	}


}
enum ModernType
{
	MOD_CYLINDER,
	MOD_HELIX,
	MOD_NARROW,
	MOD_CONED
};
void Building::GenModern()
{
	int ModType=rand()%3;
	int maxheight = m_vArea.y;
	m_Height = ((rand()%(maxheight/2)+1)+(maxheight/2));

	HRESULT hr;
	int NumTris=36;
	int NumVerts=38;
	int ConeTiers;
	int ConeVerts;
	int ConeTris;
	LPD3DXMESH base;
	LPD3DXMESH cap;
	LPD3DXMESH sides;

	D3DXCreateMesh(NumTris,NumVerts,D3DXMESH_MANAGED,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&base);
	D3DXCreateMesh(NumTris,NumVerts,D3DXMESH_MANAGED,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&cap);
	D3DXCreateMesh(NumTris*2,(NumVerts*2)-2,D3DXMESH_MANAGED,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&sides);

	POSNORMUVVERTEX* pBaseVertices;
	POSNORMUVVERTEX* pSidesVertices;
	POSNORMUVVERTEX* pCapVertices;
	base->LockVertexBuffer(0, (void**)&pBaseVertices);
	sides->LockVertexBuffer(0,(void**)&pSidesVertices);
	cap->LockVertexBuffer(0,(void**)&pCapVertices);
	int scount=0;

	switch(ModType)
	{
	case MOD_CYLINDER:

		//centre of circle
		pBaseVertices[0].Pos=D3DXVECTOR3(0,0,0);
		pBaseVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pBaseVertices[0].UV=D3DXVECTOR2(0,0);

		pCapVertices[0].Pos=D3DXVECTOR3(0,m_Height,0);
		pCapVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pCapVertices[0].UV=D3DXVECTOR2(0,0);

		//points around circle
		for (unsigned int i=0;i<NumVerts-1;++i)
		{
			float x = -sin((i*10)*(M_PI/180))*(m_vArea.x/2);
			float z = cos((i*10)*(M_PI/180))*(m_vArea.z/2);
			pBaseVertices[i+1].Pos=D3DXVECTOR3(x,0,z);
			pBaseVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pBaseVertices[i+1].UV=D3DXVECTOR2(0,0);

			pCapVertices[i+1].Pos=D3DXVECTOR3(x,m_Height,z);
			pCapVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pCapVertices[i+1].UV=D3DXVECTOR2(0,0);
		}

		break;

	
	case MOD_CONED:
		ConeTiers=9;
		ConeVerts=NumVerts+(ConeTiers*(NumVerts-1))-10;
		ConeTris=NumTris+(ConeTiers*(NumTris*2));
		V(D3DXCreateMesh(ConeTris*2,ConeVerts,D3DXMESH_MANAGED,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&cap));
		V(cap->LockVertexBuffer(0,(void**)&pCapVertices));
		//centre of circle
		pBaseVertices[0].Pos=D3DXVECTOR3(0,0,0);
		pBaseVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pBaseVertices[0].UV=D3DXVECTOR2(0,0);

		pCapVertices[0].Pos=D3DXVECTOR3(0,m_Height,0);
		pCapVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pCapVertices[0].UV=D3DXVECTOR2(0,0);

		//points around circle
		for (unsigned int i=0;i<NumVerts-1;++i)
		{
			float x = -sin((i*10)*(M_PI/180))*(m_vArea.x/2);
			float z = cos((i*10)*(M_PI/180))*(m_vArea.z/2);
			pBaseVertices[i+1].Pos=D3DXVECTOR3(x,0,z);
			pBaseVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pBaseVertices[i+1].UV=D3DXVECTOR2(0,0);

			pCapVertices[i+1].Pos=D3DXVECTOR3(x,m_Height,z);
			pCapVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pCapVertices[i+1].UV=D3DXVECTOR2(0,0);
		}
		
		for(unsigned int i=1;i<ConeTiers+1;++i)
		{
			float additionalHeight = (i*(m_Height/(ConeTiers*5)));
			for(unsigned int j=0;j<NumVerts-1;++j)
			{
				float x;
				float z;
				if(i<ConeTiers)
				{
					float r = ((float)i*((float)90/ConeTiers))*(M_PI/180);
					x = -sin((j*10)*(M_PI/180))*((m_vArea.x/2)*cos(r));
					z = cos((j*10)*(M_PI/180))*((m_vArea.x/2)*cos(r));
				}
				else
				{
					x=0;
					z=0;
				}
				pCapVertices[j+(i*NumTris)+1].Pos=D3DXVECTOR3(x,m_Height+additionalHeight,z);
				pCapVertices[j+(i*NumTris)+1].Norm=D3DXVECTOR3(0,1,0);
				pCapVertices[j+(i*NumTris)+1].UV=D3DXVECTOR2(0,0);
			}

		}

		break;

	
	float helixStart;
	float helixStep;
	case MOD_HELIX:
		
		helixStart=((rand()%m_Height));
		helixStep=(m_Height-helixStart)/NumTris;
		//centre of circle
		pBaseVertices[0].Pos=D3DXVECTOR3(0,0,0);
		pBaseVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pBaseVertices[0].UV=D3DXVECTOR2(0,0);

		pCapVertices[0].Pos=D3DXVECTOR3(0,m_Height,0);
		pCapVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pCapVertices[0].UV=D3DXVECTOR2(0,0);

		//points around circle
		for (unsigned int i=0;i<NumVerts-1;++i)
		{
			float x = -sin((i*10)*(M_PI/180))*(m_vArea.x/2);
			float z = cos((i*10)*(M_PI/180))*(m_vArea.z/2);
			pBaseVertices[i+1].Pos=D3DXVECTOR3(x,0,z);
			pBaseVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pBaseVertices[i+1].UV=D3DXVECTOR2(0,0);

			pCapVertices[i+1].Pos=D3DXVECTOR3(x,helixStart+(helixStep*i),z);
			pCapVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pCapVertices[i+1].UV=D3DXVECTOR2(0,0);
		}
		break;

	int halfWidthHun;
	float topDecrease;
	case MOD_NARROW:

		halfWidthHun=(m_vArea.x/2)*100;
		topDecrease= (float)((rand()%halfWidthHun)/100);
		//centre of circle
		pBaseVertices[0].Pos=D3DXVECTOR3(0,0,0);
		pBaseVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pBaseVertices[0].UV=D3DXVECTOR2(0,0);

		pCapVertices[0].Pos=D3DXVECTOR3(0,m_Height,0);
		pCapVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pCapVertices[0].UV=D3DXVECTOR2(0,0);

		//points around circle
		for (unsigned int i=0;i<NumVerts-1;++i)
		{
			float x = -sin((i*10)*(M_PI/180))*(m_vArea.x/2);
			float z = cos((i*10)*(M_PI/180))*(m_vArea.z/2);
			float x2 = -sin((i*10)*(M_PI/180))*((m_vArea.x/2)-topDecrease);
			float z2 = cos((i*10)*(M_PI/180))*((m_vArea.z/2)-topDecrease);
			pBaseVertices[i+1].Pos=D3DXVECTOR3(x,0,z);
			pBaseVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pBaseVertices[i+1].UV=D3DXVECTOR2(0,0);

			pCapVertices[i+1].Pos=D3DXVECTOR3(x2,m_Height,z2);
			pCapVertices[i+1].Norm=D3DXVECTOR3(0,1,0);
			pCapVertices[i+1].UV=D3DXVECTOR2(0,0);
		}
		break;
	}

	//make 2 verts for each vert on the circle
	for (unsigned int i=1;i<NumVerts;++i)
	{
		pSidesVertices[scount].Pos=pBaseVertices[i].Pos;
		pSidesVertices[scount].Norm=D3DXVECTOR3(0,1,0);
		pSidesVertices[scount].UV=D3DXVECTOR2(0,0);
		scount++;
		pSidesVertices[scount].Pos=pCapVertices[i].Pos;
		pSidesVertices[scount].Norm=D3DXVECTOR3(0,1,0);
		pSidesVertices[scount].UV=D3DXVECTOR2(0,0);
		scount++;

	}
	V(base->UnlockVertexBuffer());
	V(sides->UnlockVertexBuffer());
	V(cap->UnlockVertexBuffer());

	WORD *BaseIndexData=NULL;
	V(base->LockIndexBuffer( 0, (void**)&BaseIndexData ));
	WORD *CapIndexData=NULL;
	V(cap->LockIndexBuffer( 0, (void**)&CapIndexData ));
	int indCount=0;
	for(unsigned int i=0; i<NumTris;++i)
	{
		BaseIndexData[indCount]=0;
		CapIndexData[indCount]=0;
		indCount++;
		BaseIndexData[indCount]=i+1;
		CapIndexData[indCount]=i+1;
		indCount++;
		if(i!=NumTris-1)
		{
			BaseIndexData[indCount]=i+2;
			CapIndexData[indCount]=i+2;
			indCount++;
		}
		else
		{
			BaseIndexData[indCount]=1;
			CapIndexData[indCount]=1;
			indCount++;
		}

	}
	V(base->UnlockIndexBuffer());

	if(ModType==MOD_CONED)
	{
		for(unsigned int i=NumTris;i<ConeTris;i++)
		{
			CapIndexData[indCount++]=i+1;
			CapIndexData[indCount++]=i+1+NumTris;
			CapIndexData[indCount++]=i+2;

			CapIndexData[indCount++]=i+2;
			CapIndexData[indCount++]=i+1+NumTris;
			CapIndexData[indCount++]=i+2+NumTris;

			/*
			CapIndexData[indCount++]=i+2;
			CapIndexData[indCount++]=i+2+NumTris;
			CapIndexData[indCount++]=i+3;

			CapIndexData[indCount++]=i+3;
			CapIndexData[indCount++]=i+2+NumTris;
			CapIndexData[indCount++]=i+3+NumTris;
			*/
		}
	}
	V(cap->UnlockIndexBuffer());


	WORD *SidesIndexData=NULL;
	V(sides->LockIndexBuffer( 0, (void**)&SidesIndexData ));
	indCount=0;
	for(unsigned int i=0; i<NumTris*2;i+=2)
	{
		if(i<(NumTris*2)-1)
		{
			SidesIndexData[indCount++]=i;
			SidesIndexData[indCount++]=i+1;
			SidesIndexData[indCount++]=i+2;

			SidesIndexData[indCount++]=i+2;
			SidesIndexData[indCount++]=i+1;
			SidesIndexData[indCount++]=i+3;
		}
		else
		{
			SidesIndexData[indCount++]=i;
			SidesIndexData[indCount++]=i+1;
			SidesIndexData[indCount++]=0;

			SidesIndexData[indCount++]=0;
			SidesIndexData[indCount++]=i+1;
			SidesIndexData[indCount++]=1;
		}
	}
	V(sides->UnlockIndexBuffer());

	LPD3DXMESH pMeshes[3];
	pMeshes[0]=base;
	pMeshes[1]=sides;
	pMeshes[2]=cap;
	D3DXMATRIX TransMatrix[3];
	D3DXMatrixIdentity(&TransMatrix[0]);
	D3DXMatrixIdentity(&TransMatrix[1]);
	D3DXMatrixIdentity(&TransMatrix[2]);
	
	m_NumSubsets+=2;
	V(D3DXConcatenateMeshes(pMeshes,3,D3DXMESH_MANAGED,TransMatrix,0,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&m_pMesh));
	
	//m_pMesh=cap;
}

void Building::GenClassic()
{  
	HRESULT hr;
	bool narrow = rand()%2;
	int narAmount=(rand()%5)+4;
	int narStepX=m_vArea.x/narAmount;
	int narStepZ=m_vArea.z/narAmount;
	int maxheight=m_vArea.y;
	m_Height = (rand()%(maxheight/2))+(maxheight/2);
	int maxTierSize=m_Height/10;
	float tierSize=(rand()%maxTierSize)+1;
	float lip = (m_vArea.x/100)*(rand()%4);
	LPD3DXMESH mesh1;
	LPD3DXMESH mesh2;
	D3DXMATRIX TransMatrix[3];
	V(D3DXCreateBox(TheD3DApp::Instance()->GetD3DDev(),m_vArea.x,tierSize,m_vArea.z,&m_pMesh,0));
	D3DXMatrixTransformation(&TransMatrix[0],0,0,0,0,0,&D3DXVECTOR3(0,(tierSize/2),0));
	int top=tierSize;
	int tierNum=0;
	while(top<m_Height-(tierSize*2))
	{
		int x =m_vArea.x;
		int z =m_vArea.z;
		if(narrow)
		{
			tierSize=tierSize/(narAmount-3);
			if(tierSize<1)
			{
				tierSize=1;
			}
			int newx=narStepX*tierNum;
			if(newx<(m_vArea.x/2))
			{
				x-=newx;
			}
			else
			{
				x=1;
			}
			int newz=narStepZ*tierNum;
			if(newz<(m_vArea.z/2))
			{
				z-=newz;
			}
			else
			{
				z=1;
			}
		}
		int rel = ((rand()%4)+1);
		int maxSecSize = (m_Height-(top+tierSize));
		int secSize = maxSecSize/rel;
		if(secSize==0)
		{
			secSize=tierSize;
		}
		
		V(D3DXCreateBox(TheD3DApp::Instance()->GetD3DDev(),x,secSize,z,&mesh1,0));
		V(D3DXCreateBox(TheD3DApp::Instance()->GetD3DDev(),x+lip,tierSize,z+lip,&mesh2,0));
		
		LPD3DXMESH pMeshes[3];
		pMeshes[0]=m_pMesh;
		pMeshes[1]=mesh1;
		pMeshes[2]=mesh2;
		float y1=(float)top+((float)secSize/2);
		float y2=(float)top+secSize+((float)tierSize/2);
		D3DXMatrixTransformation(&TransMatrix[1],0,0,0,0,0,&D3DXVECTOR3(0,y1,0));
		D3DXMatrixTransformation(&TransMatrix[2],0,0,0,0,0,&D3DXVECTOR3(0,y2,0));

		V(D3DXConcatenateMeshes(pMeshes,3,D3DXMESH_MANAGED,TransMatrix,0,NULL,TheD3DApp::Instance()->GetD3DDev(),&m_pMesh));

		D3DXMatrixIdentity(&TransMatrix[0]);
		
		top+=(secSize+tierSize);
		m_NumSubsets+=2;
		tierNum++;
		if(x==1 || z==1)
		{
			top=m_Height;
		}
	}
	//consider spike
	bool spike = rand()%2;
	if (!narrow && spike)
	{
		V(D3DXCreateMesh(4,5,D3DXMESH_MANAGED,TheD3DApp::Instance()->GetVertexElements(),TheD3DApp::Instance()->GetD3DDev(),&mesh1));
		POSNORMUVVERTEX* pVertices;
		V(mesh1->LockVertexBuffer(0,(void**)&pVertices));
		pVertices[0].Pos=D3DXVECTOR3(0,tierSize,0);
		pVertices[0].Norm=D3DXVECTOR3(0,1,0);
		pVertices[0].UV=D3DXVECTOR2(0,0);

		pVertices[1].Pos=D3DXVECTOR3((m_vArea.x/2),0,(m_vArea.z/2));
		pVertices[1].Norm=D3DXVECTOR3(0,1,0);
		pVertices[1].UV=D3DXVECTOR2(0,0);
		pVertices[2].Pos=D3DXVECTOR3(-(m_vArea.x/2),0,(m_vArea.z/2));
		pVertices[2].Norm=D3DXVECTOR3(0,1,0);
		pVertices[2].UV=D3DXVECTOR2(0,0);
		pVertices[3].Pos=D3DXVECTOR3(-(m_vArea.x/2),0,-(m_vArea.z/2));
		pVertices[3].Norm=D3DXVECTOR3(0,1,0);
		pVertices[3].UV=D3DXVECTOR2(0,0);
		pVertices[4].Pos=D3DXVECTOR3((m_vArea.x/2),0,-(m_vArea.z/2));
		pVertices[4].Norm=D3DXVECTOR3(0,1,0);
		pVertices[5].UV=D3DXVECTOR2(0,0);
		V(mesh1->UnlockVertexBuffer());

		WORD *IndexData=NULL;
		int indcount=0;
		V(mesh1->LockIndexBuffer( 0, (void**)&IndexData ));
		IndexData[indcount++]=0;
		IndexData[indcount++]=1;
		IndexData[indcount++]=2;

		IndexData[indcount++]=0;
		IndexData[indcount++]=2;
		IndexData[indcount++]=3;

		IndexData[indcount++]=0;
		IndexData[indcount++]=3;
		IndexData[indcount++]=4;

		IndexData[indcount++]=0;
		IndexData[indcount++]=4;
		IndexData[indcount++]=1;
		V(mesh1->UnlockIndexBuffer());


		D3DXMATRIX TransM[2];
		D3DXMatrixIdentity(&TransM[0]);
		D3DXMatrixTransformation(&TransM[1],0,0,0,0,0,&D3DXVECTOR3(0,top,0));
		LPD3DXMESH pMeshes[2];
		pMeshes[0]=m_pMesh;
		pMeshes[1]=mesh1;
		V(D3DXConcatenateMeshes(pMeshes,2,D3DXMESH_MANAGED,TransM,0,NULL,TheD3DApp::Instance()->GetD3DDev(),&m_pMesh));
		m_NumSubsets++;
	}

}
void Building::NextStep()
{
	m_MaxStep++;
	//GenBlocky();
}